import Knowledge, { IKnowledge, KnowledgeStatus, KnowledgeType } from '@database/models/Knowledge';
import logger from '@utils/logger';
import mongoose from 'mongoose';

interface KnowledgeFilter {
  search?: string;
  category?: string;
  tags?: string[];
  status?: KnowledgeStatus;
  type?: KnowledgeType;
}

interface CreateKnowledgeDto {
  title: string;
  content: string;
  summary: string;
  type: KnowledgeType;
  category: string;
  tags: string[];
  status: KnowledgeStatus;
  relatedAlerts: string[];
  author: string;
  authorName: string;
}

interface UpdateKnowledgeDto {
  title?: string;
  content?: string;
  summary?: string;
  type?: KnowledgeType;
  category?: string;
  tags?: string[];
  relatedAlerts?: string[];
}

/**
 * Knowledge Service
 */
class KnowledgeService {
  /**
   * Get knowledge documents with filtering and pagination
   */
  async getKnowledgeDocs(
    filter: KnowledgeFilter,
    page: number = 1,
    pageSize: number = 20
  ): Promise<{ docs: IKnowledge[]; total: number }> {
    try {
      const query: any = {};

      // Search filter (full-text search)
      if (filter.search) {
        query.$text = { $search: filter.search };
      }

      // Category filter
      if (filter.category) {
        query.category = filter.category;
      }

      // Tags filter
      if (filter.tags && filter.tags.length > 0) {
        query.tags = { $in: filter.tags };
      }

      // Status filter
      if (filter.status) {
        query.status = filter.status;
      } else {
        // Default: only show published docs to non-admin users
        query.status = KnowledgeStatus.PUBLISHED;
      }

      // Type filter
      if (filter.type) {
        query.type = filter.type;
      }

      const skip = (page - 1) * pageSize;

      const [docs, total] = await Promise.all([
        Knowledge.find(query)
          .sort({ createdAt: -1 })
          .skip(skip)
          .limit(pageSize)
          .populate('author', 'username email')
          .lean(),
        Knowledge.countDocuments(query),
      ]);

      return { docs: docs as IKnowledge[], total };
    } catch (error) {
      logger.error('Failed to get knowledge docs:', error as Error);
      throw error;
    }
  }

  /**
   * Get knowledge document by ID
   */
  async getKnowledgeDocById(id: string, incrementView: boolean = true): Promise<IKnowledge | null> {
    try {
      const doc = await Knowledge.findById(id)
        .populate('author', 'username email')
        .populate('contributors.userId', 'username email');

      if (!doc) {
        return null;
      }

      // Increment view count
      if (incrementView) {
        await doc.incrementViewCount();
      }

      return doc;
    } catch (error) {
      logger.error(`Failed to get knowledge doc ${id}:`, error as Error);
      throw error;
    }
  }

  /**
   * Create knowledge document
   */
  async createKnowledgeDoc(data: CreateKnowledgeDto): Promise<IKnowledge> {
    try {
      const doc = new Knowledge({
        title: data.title,
        content: data.content,
        summary: data.summary,
        type: data.type,
        category: data.category,
        tags: data.tags,
        status: data.status,
        relatedAlerts: data.relatedAlerts,
        author: new mongoose.Types.ObjectId(data.author),
        authorName: data.authorName,
      });

      await doc.save();

      logger.info(`Knowledge document created: ${doc._id}`);
      return doc;
    } catch (error) {
      logger.error('Failed to create knowledge doc:', error as Error);
      throw error;
    }
  }

  /**
   * Update knowledge document
   */
  async updateKnowledgeDoc(
    id: string,
    data: UpdateKnowledgeDto,
    userId?: string,
    userName?: string
  ): Promise<IKnowledge | null> {
    try {
      const doc = await Knowledge.findById(id);

      if (!doc) {
        return null;
      }

      // Update fields
      if (data.title !== undefined) doc.title = data.title;
      if (data.summary !== undefined) doc.summary = data.summary;
      if (data.type !== undefined) doc.type = data.type;
      if (data.category !== undefined) doc.category = data.category;
      if (data.tags !== undefined) doc.tags = data.tags;
      if (data.relatedAlerts !== undefined) doc.relatedAlerts = data.relatedAlerts;

      // If content is updated, save version history
      if (data.content !== undefined && data.content !== doc.content && userId) {
        await doc.updateContent(data.content, new mongoose.Types.ObjectId(userId));
      } else if (data.content !== undefined) {
        doc.content = data.content;
        await doc.save();
      } else {
        await doc.save();
      }

      // Add contributor if not author
      if (userId && userName && doc.author.toString() !== userId) {
        const existingContributor = doc.contributors.find(
          (c) => c.userId.toString() === userId
        );

        if (!existingContributor) {
          doc.contributors.push({
            userId: new mongoose.Types.ObjectId(userId),
            userName,
            contributedAt: new Date(),
          });
          await doc.save();
        }
      }

      logger.info(`Knowledge document updated: ${id}`);
      return doc;
    } catch (error) {
      logger.error(`Failed to update knowledge doc ${id}:`, error as Error);
      throw error;
    }
  }

  /**
   * Delete knowledge document
   */
  async deleteKnowledgeDoc(id: string): Promise<boolean> {
    try {
      const result = await Knowledge.findByIdAndDelete(id);

      if (!result) {
        return false;
      }

      logger.info(`Knowledge document deleted: ${id}`);
      return true;
    } catch (error) {
      logger.error(`Failed to delete knowledge doc ${id}:`, error as Error);
      throw error;
    }
  }

  /**
   * Publish knowledge document
   */
  async publishKnowledgeDoc(id: string): Promise<IKnowledge | null> {
    try {
      const doc = await Knowledge.findById(id);

      if (!doc) {
        return null;
      }

      await doc.publish();

      logger.info(`Knowledge document published: ${id}`);
      return doc;
    } catch (error) {
      logger.error(`Failed to publish knowledge doc ${id}:`, error as Error);
      throw error;
    }
  }

  /**
   * Archive knowledge document
   */
  async archiveKnowledgeDoc(id: string): Promise<IKnowledge | null> {
    try {
      const doc = await Knowledge.findById(id);

      if (!doc) {
        return null;
      }

      await doc.archive();

      logger.info(`Knowledge document archived: ${id}`);
      return doc;
    } catch (error) {
      logger.error(`Failed to archive knowledge doc ${id}:`, error as Error);
      throw error;
    }
  }

  /**
   * Get all unique categories
   */
  async getCategories(): Promise<string[]> {
    try {
      const categories = await Knowledge.distinct('category', {
        status: KnowledgeStatus.PUBLISHED,
      });

      return categories;
    } catch (error) {
      logger.error('Failed to get categories:', error as Error);
      throw error;
    }
  }

  /**
   * Get popular documents
   */
  async getPopularDocs(limit: number = 10): Promise<IKnowledge[]> {
    try {
      const docs = await Knowledge.find({
        status: KnowledgeStatus.PUBLISHED,
      })
        .sort({ viewCount: -1, averageRating: -1 })
        .limit(limit)
        .populate('author', 'username email')
        .lean();

      return docs as IKnowledge[];
    } catch (error) {
      logger.error('Failed to get popular docs:', error as Error);
      throw error;
    }
  }

  /**
   * Ask AI question about knowledge base
   */
  async askAI(
    question: string,
    context?: string
  ): Promise<{
    answer: string;
    relatedDocs: IKnowledge[];
    confidence: number;
    sources: string[];
  }> {
    try {
      // Search for relevant documents
      const relatedDocs = await Knowledge.find({
        status: KnowledgeStatus.PUBLISHED,
        $text: { $search: question },
      })
        .sort({ score: { $meta: 'textScore' } })
        .limit(5)
        .populate('author', 'username email')
        .lean();

      // Build answer from related documents
      let answer = '';
      if (relatedDocs.length > 0) {
        const topDoc = relatedDocs[0] as any;
        answer = `Based on our knowledge base, here's what I found:\n\n${topDoc.summary}\n\nFor more details, please refer to "${topDoc.title}".`;
      } else {
        answer = `I couldn't find specific information about "${question}" in the knowledge base. Please try rephrasing your question or contact support for assistance.`;
      }

      return {
        answer,
        relatedDocs: relatedDocs as IKnowledge[],
        confidence: relatedDocs.length > 0 ? 0.8 : 0.3,
        sources: relatedDocs.map((doc: any) => doc.title),
      };
    } catch (error) {
      logger.error('Failed to answer AI question:', error as Error);
      throw error;
    }
  }

  /**
   * Add rating to knowledge document
   */
  async addRating(
    id: string,
    userId: string,
    userName: string,
    rating: number,
    comment?: string
  ): Promise<IKnowledge | null> {
    try {
      const doc = await Knowledge.findById(id);

      if (!doc) {
        return null;
      }

      await doc.addRating(new mongoose.Types.ObjectId(userId), userName, rating, comment);

      logger.info(`Rating added to knowledge document ${id} by ${userName}`);
      return doc;
    } catch (error) {
      logger.error(`Failed to add rating to knowledge doc ${id}:`, error as Error);
      throw error;
    }
  }
}

export default new KnowledgeService();
